//!
/**
 ***********************************************************************************************************
 * <RBD3D11Renderer implementation>
 ***********************************************************************************************************
 * @file RBD3D11Renderer.cpp
 ***********************************************************************************************************/

/************************************************************************************************************
 * Includes
 ************************************************************************************************************/
#include "RBD3D11Renderer.h"

#include <d3d11.h>
#include "Effects11/Inc/d3dx11effect.h"

#include "RBRenderProcess/RBBlendState.h"
#include "RBRenderProcess/RBDepthStencilState.h"
#include "RBRenderProcess/RBDepthStencilView.h"
#include "RBRenderProcess/RBEffect.h"
#include "RBRenderProcess/RBEffectFactory.h"
#include "RBRenderProcess/RBEffectTechnique.h"
#include "RBRenderProcess/RBEffectPass.h"
#include "RBRenderProcess/RBShader.h"
#include "RBRenderProcess/RBRasterizerState.h"
#include "RBRenderProcess/RBRenderTargetView.h"

#include "RBMain/RBCamera.h"
#include "RBMain/RBRenderTargetGroup.h"
#include "RBMain/RBVertexBuffer.h"
#include "RBMain/RBVertexDeclaration.h"
#include "RBMain/RBIndexBuffer.h"
#include "RBMain/RBViewport.h"

#include "RBD3D11EffectCompiler.h"
#include "RBD3D11HardwareBufferManager.h"
#include "RBD3D11Mappings.h"

/************************************************************************************************************/
static const float4x4 g_halfOffsetScale(1.0f,0.0f,0.0f,0.0f,
                                        0.0f,1.0f,0.0f,0.0f,
                                        0.0f,0.0f,0.5f,0.5f,
                                        0.0f,0.0f,0.0f,1.0f);

float32 RBRenderer::ms_near = 0.0f;

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBD3D11Renderer::RBD3D11Renderer()
: m_swap_chain(NULL)
, m_device(NULL)
, m_immediate_context(NULL)
{
    m_ProjOffsetScale = g_halfOffsetScale;

}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
RBD3D11Renderer::~RBD3D11Renderer()
{

}
/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
bool RBD3D11Renderer::init(ID3D11Device* device, IDXGISwapChain* swap_chain)
{
    if (!init_common(device))
    {
        return false;
    }

    m_swap_chain = swap_chain;

    create_back_buffer();

    return true;

}

bool RBD3D11Renderer::init_no_swap_chain( ID3D11Device* device, const RBD3D11RendererInit& init )
{
    if (!init_common(device))
    {
        return false;
    }

    m_init_params = init;

    create_back_buffer();

    return true;
}


bool RBD3D11Renderer::init_common( ID3D11Device* device )
{
    if (!RBRenderer::Init())
    {
        return false;
    }

    m_device = device;
    m_device->GetImmediateContext(&m_immediate_context);

    RBEffectFactory::GetSingleton()->AddCompiler(new RBD3D11EffectCompiler(this));
    m_pHBMgr = new RBD3D11HardwareBufferManager(m_device);

    RBEffect::Init();

    ID3D11RasterizerState* pRSState;
    D3D11_RASTERIZER_DESC d3dDefaultRSState = 
    {
        D3D11_FILL_SOLID,       // FillMode
        D3D11_CULL_BACK,        // CullMode
        TRUE,                   // FrontCounterClockwise
        D3D11_DEFAULT_DEPTH_BIAS,                      // DepthBias
        D3D11_DEFAULT_DEPTH_BIAS_CLAMP,                   // DepthBiasClamp
        D3D11_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,  // SlopeScaledDepthBias
        TRUE,                   // DepthClipEnable
        FALSE,                  // ScissorEnable
        FALSE,                  // MultisampleEnable
        FALSE                   // AntialiasedLineEnable
    };

    m_device->CreateRasterizerState(&d3dDefaultRSState, &pRSState);
    m_immediate_context->RSSetState(pRSState);

    return true;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::Release()
{
    delete m_pBackBuffer;
    delete m_pDepthStencil;

    RBCommon::SafeRelease(m_immediate_context);
    RBCommon::SafeRelease(m_swap_chain);
    RBCommon::SafeRelease(m_device);
    RBRenderer::Release();
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
ID3D11Device* RBD3D11Renderer::GetDevice() const
{
    return m_device;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::IASetVertexBuffer( uint32 uiStream, const RBVertexBuffer* pVertexBuffer, bool bUseInstancing /*= false*/ )
{
    UNUSED(bUseInstancing);
    UNUSED(bUseInstancing);
    const UINT pStrides[] = {pVertexBuffer->GetVertexSize()};
    const UINT pOffsets[] = {0};
    ID3D11Buffer* pBuffer[] = {(ID3D11Buffer*)pVertexBuffer->GetNative()};
    m_immediate_context->IASetVertexBuffers(uiStream, 1, pBuffer, pStrides, pOffsets);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::IASetVertexDecl( const RBVertexDeclaration* pVertexDecl )
{
    m_immediate_context->IASetInputLayout((ID3D11InputLayout*)pVertexDecl->GetNative());
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::IASetIndexBuffer( const RBIndexBuffer* pIndexBuffer )
{
    DXGI_FORMAT d3dIdxFormat = (pIndexBuffer->GetIndexType() == RBIndexBufferType::IT_16BIT) ? DXGI_FORMAT_R16_UINT : DXGI_FORMAT_R32_UINT;
    m_immediate_context->IASetIndexBuffer((ID3D11Buffer*)pIndexBuffer->GetNative(), d3dIdxFormat, 0);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::IASetTopology( RBGeometryPrimitiveType::EType ePT )
{
    switch (ePT)
    {
    case RBGeometryPrimitiveType::TRIANGLELIST:
        m_immediate_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
        break;
    case RBGeometryPrimitiveType::LINELIST:
        m_immediate_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST);
    default: return;
    }

}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::RSSetViewport( const RBViewPort& viewport )
{
    D3D11_VIEWPORT d3dViewport = 
    {
        viewport.X,
        viewport.Y,
        viewport.Width,
        viewport.Height,
        viewport.ZMin,
        viewport.ZMax
    };

    m_immediate_context->RSSetViewports(1, &d3dViewport);

}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::OMSetRenderTargets( uint32 view_count, const RBRenderTargetView* const* colors, RBDepthStencilView* depth )
{
    ID3D11RenderTargetView* pViews[RBRenderTargetGroup::ms_maxColorBuffer] = {0};
    for (uint32 ui=0; ui < view_count; ++ui)
    {
        pViews[ui] = (ID3D11RenderTargetView*)colors[ui]->GetNative();
    }

    ID3D11DepthStencilView* pDepthStencilView = (depth != NULL) ? (ID3D11DepthStencilView*)depth->GetNative() : 
                                                                   NULL;

    m_immediate_context->OMSetRenderTargets(view_count, pViews, pDepthStencilView);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::ClearRenderTargets( RBRenderTargetGroup* pRTT, const RBColorA& color, float32 clearZ, uint32 clearFlags )
{
    uint32 uiRTCount = pRTT->GetBufferCount();
    if (clearFlags & CF_COLOR)
    {
        for (uint32 ui=0; ui < uiRTCount; ++ui)
        {
            m_immediate_context->ClearRenderTargetView((ID3D11RenderTargetView*)pRTT->GetRenderTargetView(ui)->GetNative(), &color.r);
        }
    }

    uint32 eClearFlags = 0;
    float32 d3dClearZ = 0;
    if (clearFlags & CF_ZBUFFER)
    {
        d3dClearZ = 0.5f - 0.5f * clearZ;
        eClearFlags |= D3D11_CLEAR_DEPTH;
    }

    if (clearFlags & CF_STENCIL)
    {
        eClearFlags |= D3D11_CLEAR_STENCIL;
    }


    if (eClearFlags != 0 && pRTT->GetDepthStencilView() != NULL)
    {
        m_immediate_context->ClearDepthStencilView((ID3D11DepthStencilView*)pRTT->GetDepthStencilView()->GetNative(), eClearFlags, d3dClearZ, 0);
    }
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::Swap()
{
    HRESULT hr;
    hr = m_swap_chain->Present(0, 0);
    RBAssert(SUCCEEDED(hr), "Problem with present");
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::DrawIndexedPrimitive( uint32 /*uiVertexCount*/, uint32 uiPrimitiveCount, int32 iBaseVertexIndex, uint32 uiStartIndex )
{
    m_immediate_context->DrawIndexed(uiPrimitiveCount*3, uiStartIndex, iBaseVertexIndex);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::DrawPrimitive( uint32 uiPrimitiveCount, uint32 uiStartVertex )
{
    m_immediate_context->Draw(uiPrimitiveCount, uiStartVertex);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::Map( void* pResource, uint32 mapType, rb_locked_data* pMapped, uint32 sub_resource )
{
    D3D11_MAPPED_SUBRESOURCE d3dMapped;
    m_immediate_context->Map((ID3D11Resource*) pResource, sub_resource, (D3D11_MAP)mapType, 0, &d3dMapped);
    pMapped->Data = d3dMapped.pData;
    pMapped->Pitch = d3dMapped.RowPitch;
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::Unmap( void* pResource, uint32 sub_resource)
{
    m_immediate_context->Unmap((ID3D11Resource*) pResource, sub_resource);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::VSSetConstantBuffers( uint32 uiStart, uint32 uiCount, void** vBuffers )
{
    m_immediate_context->VSSetConstantBuffers(uiStart, uiCount, (ID3D11Buffer**)vBuffers);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::PSSetConstantBuffers( uint32 uiStart, uint32 uiCount, void** vBuffers )
{
    m_immediate_context->PSSetConstantBuffers(uiStart, uiCount, (ID3D11Buffer**)vBuffers);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::VSSetShaderResources( uint32 uiStart, uint32 uiCount, void** vNativeViews )
{
    m_immediate_context->VSSetShaderResources(uiStart, uiCount, (ID3D11ShaderResourceView**)vNativeViews);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::PSSetShaderResources( uint32 uiStart, uint32 uiCount, void** vNativeViews )
{
    m_immediate_context->PSSetShaderResources(uiStart, uiCount, (ID3D11ShaderResourceView**)vNativeViews);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::VSSetSamplers( uint32 uiStart, uint32 uiCount, void** vNativeSamplers )
{
    m_immediate_context->VSSetSamplers(uiStart, uiCount, (ID3D11SamplerState**)vNativeSamplers);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::PSSetSamplers( uint32 uiStart, uint32 uiCount, void** vNativeSamplers )
{
    m_immediate_context->PSSetSamplers(uiStart, uiCount, (ID3D11SamplerState**)vNativeSamplers);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::VSSetShader( const RBShader* pShader )
{
    m_immediate_context->VSSetShader((ID3D11VertexShader*)pShader->Native,NULL,0);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::PSSetShader( const RBShader* pShader )
{
    m_immediate_context->PSSetShader((ID3D11PixelShader*)pShader->Native,NULL,0);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::CopyResource( RBHardwareBuffer* dst, RBHardwareBuffer* src )
{
    m_immediate_context->CopyResource((ID3D11Resource*)dst->GetNative(),(ID3D11Resource*)src->GetNative());
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::OMSetBlendState( const RBBlendState* pBlendState )
{
    const float d3dBlendFactors[] = {0.0f,0.0f,0.0f,0.0f};
    m_immediate_context->OMSetBlendState(pBlendState ? (ID3D11BlendState*)pBlendState->GetNative() : NULL, d3dBlendFactors, 0xFFFFFFFF);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::RSSetState( const RBRasterizerState* pState )
{
    m_immediate_context->RSSetState(pState ? (ID3D11RasterizerState*)pState->GetNative() : NULL);
}


/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::OMSetDepthStencilState( const RBDepthStencilState* pState )
{
    m_immediate_context->OMSetDepthStencilState(pState ? (ID3D11DepthStencilState*)pState->GetNative() : NULL, pState ? pState->StencilRef : 0);
}

/***********************************************************************************************************
 * 
 ***********************************************************************************************************/
void RBD3D11Renderer::ClearState()
{
    m_immediate_context->ClearState();
}

void RBD3D11Renderer::Flush()
{
	m_immediate_context->Flush();
}

bool RBD3D11Renderer::create_device( ID3D11Device** device)
{
	HRESULT hr = -1; ;

    D3D_DRIVER_TYPE d3dDrvType = D3D_DRIVER_TYPE_UNKNOWN;
    D3D_FEATURE_LEVEL d3dFeatureLevel;

    hr = D3D11CreateDevice
        (
        NULL, 
        D3D_DRIVER_TYPE_HARDWARE,
        NULL, 
        D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_BGRA_SUPPORT,
        NULL, 
        0,
        D3D11_SDK_VERSION,
        device,
        &d3dFeatureLevel,
        NULL
        );

	return SUCCEEDED(hr);
}

bool RBD3D11Renderer::create_device_swap_chain( ID3D11Device** device, IDXGISwapChain** swap_chain, const RBD3D11RendererInit& init )
{
    HRESULT hr = -1; ;

    IDXGIFactory* factory;
    hr = CreateDXGIFactory(__uuidof(IDXGIFactory), (void**)(&factory) );

    if (FAILED(hr))
    {
        return false;
    }

    IDXGIAdapter * pAdapter;
    int32 iAdapter=0;
    while((hr = factory->EnumAdapters(iAdapter++, &pAdapter)) != DXGI_ERROR_NOT_FOUND)
    {
        if (SUCCEEDED(hr))
        {
            break;
        }
    }

    if (FAILED(hr))
    {
        return false;
    }


    D3D_DRIVER_TYPE d3dDrvType = D3D_DRIVER_TYPE_UNKNOWN;
    D3D_FEATURE_LEVEL d3dFeatureLevel = D3D_FEATURE_LEVEL_11_0;

    DXGI_SWAP_CHAIN_DESC d3d_swap_chain_desc;
    {
        {
            d3d_swap_chain_desc.BufferDesc.Width = init.Width;
            d3d_swap_chain_desc.BufferDesc.Height = init.Height;
            d3d_swap_chain_desc.BufferDesc.RefreshRate.Numerator = 60;
            d3d_swap_chain_desc.BufferDesc.RefreshRate.Denominator = 1;
            d3d_swap_chain_desc.BufferDesc.Format = (DXGI_FORMAT) init.DisplayFormat;
            d3d_swap_chain_desc.BufferDesc.ScanlineOrdering  = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
            d3d_swap_chain_desc.BufferDesc.Scaling   = DXGI_MODE_SCALING_UNSPECIFIED;
        }
        {
            d3d_swap_chain_desc.SampleDesc.Count = 1;
            d3d_swap_chain_desc.SampleDesc.Quality = 0;
        }
        d3d_swap_chain_desc.BufferUsage = DXGI_USAGE_BACK_BUFFER | DXGI_USAGE_RENDER_TARGET_OUTPUT | DXGI_USAGE_SHADER_INPUT;
        d3d_swap_chain_desc.BufferCount = 2;
        d3d_swap_chain_desc.OutputWindow = (HWND)init.OutputWindow;
        d3d_swap_chain_desc.Windowed = init.Windowed;
        d3d_swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
        d3d_swap_chain_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
        if (!init.Windowed)
        {
            d3d_swap_chain_desc.Flags |= DXGI_SWAP_CHAIN_FLAG_NONPREROTATED;
        }
    }


    hr = D3D11CreateDeviceAndSwapChain
        (
        pAdapter, 
        D3D_DRIVER_TYPE_UNKNOWN,
        NULL, 
        D3D11_CREATE_DEVICE_SINGLETHREADED | D3D11_CREATE_DEVICE_DEBUG, 
        NULL,0, 
        D3D11_SDK_VERSION, 
        &d3d_swap_chain_desc, 
        swap_chain, 
        device, 
        NULL, 
        NULL);

    return SUCCEEDED(hr);
}


void RBD3D11Renderer::create_back_buffer()
{
    ID3D11Texture2D * d3d_back_buffer = NULL;

    if (m_swap_chain != NULL)
    {
        m_swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&d3d_back_buffer);
        d3d_back_buffer->AddRef();
    }
    else
    {
        D3D11_TEXTURE2D_DESC desc;
        desc.Width = m_init_params.Width;
        desc.Height = m_init_params.Height;
        desc.MipLevels = 1;
        desc.ArraySize = 1;
        desc.Format = (DXGI_FORMAT) m_init_params.DisplayFormat;
        desc.SampleDesc.Count = 1;
        desc.SampleDesc.Quality = 0;
        desc.Usage = D3D11_USAGE_DEFAULT;
        desc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE;
        desc.CPUAccessFlags = 0;
        desc.MiscFlags  = D3D11_RESOURCE_MISC_SHARED;
        m_device->CreateTexture2D(&desc, NULL, &d3d_back_buffer);
    }

    RBAssert(d3d_back_buffer != NULL, "no back buffer provided !");

    D3D11_TEXTURE2D_DESC desc;
    d3d_back_buffer->GetDesc(&desc);

    RBTexture* pBackBufferResource = new RBTexture(rb_hw_buffer_usage::HBU_DEFAULT | rb_hw_buffer_usage::HBU_RENDER_TARGET | rb_hw_buffer_usage::HBU_SHADER_RESOURCE, 
        rb_hw_buffer_usage::MP_GPU, m_pHBMgr, d3d_back_buffer, RBD3D11Mappings::GetRBPixelFormat(desc.Format), RBTextureType::SURFACE, 
        desc.Width, desc.Height, 1, 1, 1);

    m_pBackBuffer = m_pHBMgr->CreateRenderTargetView(pBackBufferResource);

    RBTexture* pDepthResource = m_pHBMgr->CreateRenderTarget(rb_hw_buffer_usage::HBU_DEFAULT | rb_hw_buffer_usage::HBU_DEPTHSTENCIL | rb_hw_buffer_usage::HBU_SHADER_RESOURCE,
        desc.Width, desc.Height, PF_R24G8_TYPELESS, 0);

    m_pDepthStencil = m_pHBMgr->CreateDepthStencilView(pDepthResource, PF_D24_UNORM_S8_UINT);

    m_default_target_group = RBRenderTargetGroup::Create(m_pBackBuffer, m_pDepthStencil);

}

void RBD3D11Renderer::release_back_buffer()
{
    RBCommon::SafeRelease(m_swap_chain);
    delete m_default_target_group;
    m_default_target_group = NULL;
    delete m_pDepthStencil;
    m_pDepthStencil = NULL;
    delete m_pBackBuffer;
    m_pBackBuffer = NULL;
}

ID3D11DeviceContext* RBD3D11Renderer::GetDeviceContext() const
{
    return m_immediate_context;
}

void RBD3D11Renderer::change_size( int width, int height )
{
    RBAssert(m_swap_chain==NULL, "change_size not supported with swap chain currently");
    release_back_buffer();

    m_init_params.Width = width;
    m_init_params.Height = height;

    create_back_buffer();
}
